package es.ucm.fdi.healthpred.modelo.motor;

import java.util.ArrayList;
import java.util.List;

import es.ucm.fdi.healthpred.controlador.Controlador;
import es.ucm.fdi.healthpred.controlador.excepciones.ExcepcionTransaccion;
import es.ucm.fdi.healthpred.modelo.beans.Reglas;
import es.ucm.fdi.healthpred.modelo.beans.ReglasConclusiones;
import es.ucm.fdi.healthpred.modelo.beans.ReglasPremisas;
import es.ucm.fdi.healthpred.modelo.beans.ReglasPremisasId;

/**
 * Realiza todas las gestiones (tanto l&oacute;gicas como con la BD) necesarias
 * para generar los resultados de cada nivel de inferencia, en funci&oacute;n de
 * los datos de entrada relativos al cliente y los par&aacute;metros y modo de
 * inferencia seleccionados.
 * 
 * @since 1.0
 * 
 * @version 1.0
 * 
 * @author &Aacute;lvaro G&oacute;mez Porris
 * @author V&iacute;ctor L&oacute;pez Monte
 * @author Carlos Rodr&iacute;guez Colliga
 * 
 */
public class MotorExplotacion {

	/**
	 * @param idRegla
	 * @param nivel
	 * @throws ExcepcionTransaccion
	 */
	public void activaRegla(int idRegla, int nivel) throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		consulta.activaRegla(idRegla, nivel);
	}

	/**
	 * @param premisa
	 * @param dni
	 * @return
	 * @throws ExcepcionTransaccion
	 */
	public boolean analizaPremisa(ReglasPremisas premisa, String dni)
			throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		ReglasPremisasId id = premisa.getId();
		String tabla = id.getTabla();
		String atributo = id.getAtributo();
		String valor_min = premisa.getValorMinimo();
		String valor_max = premisa.getValorMaximo();
		Object valor = consulta.devuelveValor(tabla, atributo, dni);
		if (dentroDeRango(atributo, valor, valor_min, valor_max))
			return true;
		else
			return false;
	}

	/**
	 * @param dni
	 * @param nivel
	 * @throws ExcepcionTransaccion
	 */
	public void concluir_reglas(String dni, int nivel)
			throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		List<Reglas> reglasAct = consulta.obtenReglasActivas(nivel);
		switch (nivel) {
		case 0: {
			concluyeNivel0(reglasAct, dni);
			break;
		}
		case 1: {
			concluyeNivel1(reglasAct, dni);
			break;
		}
		case 2: {
			concluyeNivel2(reglasAct, dni);
			break;
		}
		case 3: {
			concluyeNivel3(reglasAct, dni);
			break;
		}
		case 4: {
			concluyeNivel4(reglasAct, dni);
			break;
		}
		}
	}

	/**
	 * @param att
	 * @param valor
	 * @param v_min
	 * @param v_max
	 * @return
	 * @throws ExcepcionTransaccion
	 */
	public boolean dentroDeRango(String att, Object valor, String v_min,
			String v_max) throws ExcepcionTransaccion {
		try {
			String vacio = "[]";
			MotorConsulta motor = new MotorConsulta();
			Class<?> cls;
			Object[] c = new Object[1];
			Class<?>[] clasesParamSetAtt;
			clasesParamSetAtt = new Class[1];
			clasesParamSetAtt[0] = Class.forName("java.lang.String");
			if (motor.esMedia(att)) {
				if (!vacio.equals(valor.toString())) {
					c[0] = (String) valor.toString();
					cls = Class
							.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum.Sistema");
					Integer i = (Integer) cls.getMethod("getValorSistema",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_min;
					Integer j = (Integer) cls.getMethod("getValorSistema",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_max;
					Integer k = (Integer) cls.getMethod("getValorSistema",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					return (j <= i && i <= k);
				}
				return false;
			}
			if ((att.equals("codigo") || (att.equals("frecuenciaSemanal") || (att
					.equals("duracion"))))) {
				if (!vacio.equals(valor.toString())) {
					ArrayList<?> hechos = (ArrayList<?>) valor;
					boolean dentroDeRango = false;
					int i = 0;
					int aux;
					int j = Integer.valueOf(v_min);
					int k = Integer.valueOf(v_max);
					while ((!dentroDeRango) && (i < hechos.size())) {
						aux = Integer.valueOf(hechos.get(i).toString());
						if (j <= aux && aux <= k)
							dentroDeRango = true;
						else
							i++;
					}
					return dentroDeRango;
				}
				return false;
			}
			if ((att.equals("costeAnual")) || (att.equals("costeTratamiento"))) {
				if (!vacio.equals(valor.toString())) {
					ArrayList<?> coste = (ArrayList<?>) valor;
					boolean dentroDeRango = false;
					int i = 0;
					int aux = 0;
					int j = Integer.valueOf(v_min);
					int k = Integer.valueOf(v_max);
					while ((!dentroDeRango) && (i < coste.size())) {
						aux = aux + Integer.valueOf(coste.get(i).toString());
						if (j <= aux && aux <= k)
							dentroDeRango = true;
						else
							i++;
					}
					return dentroDeRango;
				}
				return false;
			}
			if (motor.esReglaNivel2(att)) {
				if (!vacio.equals(valor.toString())) {
					c[0] = (String) valor.toString();
					cls = Class
							.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum.ReglasNivel2");
					Integer i = (Integer) cls.getMethod("getValorreglasNivel2",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_min;
					Integer j = (Integer) cls.getMethod("getValorreglasNivel2",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_max;
					Integer k = (Integer) cls.getMethod("getValorreglasNivel2",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					return (j <= i && i <= k);
				}
				return false;
			}
			if (motor.esCategoriaPersona(att)) {
				if (!vacio.equals(valor.toString())) {
					c[0] = (String) valor.toString();
					cls = Class
							.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum.CategoriaPersona");
					Integer i = (Integer) cls.getMethod(
							"getValorcategoriaPersona", clasesParamSetAtt)
							.invoke(cls.newInstance(), c);
					c[0] = v_min;
					Integer j = (Integer) cls.getMethod(
							"getValorcategoriaPersona", clasesParamSetAtt)
							.invoke(cls.newInstance(), c);
					c[0] = v_max;
					Integer k = (Integer) cls.getMethod(
							"getValorcategoriaPersona", clasesParamSetAtt)
							.invoke(cls.newInstance(), c);
					return (j <= i && i <= k);
				}
				return false;
			}
			if (motor.esSiNo(att)) {
				if (!vacio.equals(valor.toString())) {
					c[0] = (String) valor.toString();
					cls = Class
							.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum.ReglasSiNo");
					Integer i = (Integer) cls.getMethod("getValorreglasSiNo",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_min;
					Integer j = (Integer) cls.getMethod("getValorreglasSiNo",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_max;
					Integer k = (Integer) cls.getMethod("getValorreglasSiNo",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					return (j <= i && i <= k);
				}
				return false;
			}
			if (motor.esNumero(att)) {
				if (!vacio.equals(valor.toString())) {
					c[0] = (String) valor.toString().subSequence(
							valor.toString().indexOf("[") + 1,
							valor.toString().indexOf("]"));
					cls = Class
							.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum.Numericos");
					Integer i = (Integer) cls.getMethod("getValornumericos",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_min;
					Integer j = (Integer) cls.getMethod("getValornumericos",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					c[0] = v_max;
					Integer k = (Integer) cls.getMethod("getValornumericos",
							clasesParamSetAtt).invoke(cls.newInstance(), c);
					return (j <= i && i <= k);
				}
				return false;
			}
			if (motor.esSistemaNivel2(att)) {
				if (!vacio.equals(valor.toString())) {
					c[0] = (String) valor.toString().subSequence(
							valor.toString().indexOf("[") + 1,
							valor.toString().indexOf("]"));
					cls = Class
							.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum.SistemasNivel2");
					Integer i = (Integer) cls.getMethod(
							"getValorsistemasNivel2", clasesParamSetAtt)
							.invoke(cls.newInstance(), c);
					c[0] = v_min;
					Integer j = (Integer) cls.getMethod(
							"getValorsistemasNivel2", clasesParamSetAtt)
							.invoke(cls.newInstance(), c);
					c[0] = v_max;
					Integer k = (Integer) cls.getMethod(
							"getValorsistemasNivel2", clasesParamSetAtt)
							.invoke(cls.newInstance(), c);
					return (j <= i && i <= k);
				}
				return false;
			}
			if (!vacio.equals(valor.toString())) {
				c[0] = (String) valor.toString().subSequence(
						valor.toString().indexOf("[") + 1,
						valor.toString().indexOf("]"));
				cls = Class
						.forName("es.ucm.fdi.healthpred.controlador.utils.compareenum."
								+ att.substring(0, 1).toUpperCase()
								+ att.substring(1));
				Integer i = (Integer) cls.getMethod("getValor" + att,
						clasesParamSetAtt).invoke(cls.newInstance(), c);
				c[0] = v_min;
				Integer j = (Integer) cls.getMethod("getValor" + att,
						clasesParamSetAtt).invoke(cls.newInstance(), c);
				c[0] = v_max;
				Integer k = (Integer) cls.getMethod("getValor" + att,
						clasesParamSetAtt).invoke(cls.newInstance(), c);
				return (j <= i && i <= k);
			}
			return false;
		} catch (Exception e) {
			e.printStackTrace();
			throw new ExcepcionTransaccion(e);
		}
	}

	/**
	 * @param dni
	 * @param nivel
	 * @throws ExcepcionTransaccion
	 */
	public void filtrar_reglas(String dni, int nivel)
			throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		consulta.desactivaReglas(nivel);
		List<ReglasPremisas> premisas = consulta.obtenReglasPremisas(nivel);
		int idAnterior = -1;
		int i = 0;
		boolean aplicable = false;
		while (i < premisas.size()) {
			ReglasPremisas p = premisas.get(i);
			ReglasPremisasId idRegla = p.getId();
			int idActual = idRegla.getIdRegla();
			if (idAnterior == idActual) {
				if (aplicable)
					aplicable = analizaPremisa(p, dni);
			} else {
				if (aplicable)
					activaRegla(idAnterior, nivel);
				aplicable = analizaPremisa(p, dni);
			}
			idAnterior = idActual;
			i++;
		}
		if (aplicable)
			activaRegla(idAnterior, nivel);
	}

	/**
	 * @param dni
	 * @param sistemas
	 * @param inferencias
	 * @throws ExcepcionTransaccion
	 */
	public void guardaPerfilClinico(String dni, int[] sistemas,
			int[] inferencias) throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		String[] cat = new String[13];
		for (int i = 0; i < 13; i++) {
			if (inferencias[i] == 0)
				cat[i] = "Sano";
			else {
				if (sistemas[i] == 0)
					cat[i] = "Sano";
				if ((sistemas[i] > 0) && (sistemas[i] <= 5))
					cat[i] = "Leve";
				if ((sistemas[i] > 5) && (sistemas[i] <= 11))
					cat[i] = "Moderado";
				if ((sistemas[i] > 11) && (sistemas[i] <= 23))
					cat[i] = "Grave";
				if (sistemas[i] > 23)
					cat[i] = "Critico";
			}
		}
		consulta.nuevoPerfilClinico(dni, cat);
	}

	/**
	 * @param dni
	 * @param categorias
	 * @param inferencias
	 * @throws ExcepcionTransaccion
	 */
	public void guardaPerfilPersona(String dni, int[] categorias,
			int[] inferencias) throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		String[] cat = new String[4];
		for (int i = 0; i < 4; i++) {
			if (inferencias[i] == 0)
				cat[i] = "Media";
			else {
				int valor = categorias[i] / inferencias[i];
				switch (valor) {
				case 1: {
					cat[i] = "Muy Baja";
					break;
				}
				case 2: {
					cat[i] = "Baja";
					break;
				}
				case 3: {
					cat[i] = "Media-Baja";
					break;
				}
				case 4: {
					cat[i] = "Media";
					break;
				}
				case 5: {
					cat[i] = "Media-Alta";
					break;
				}
				case 6: {
					cat[i] = "Alta";
					break;
				}
				case 7: {
					cat[i] = "Muy Alta";
					break;
				}
				default:
					cat[i] = "Media";
				}
			}
		}
		consulta.nuevoPerfilPersona(dni, cat);
	}

	/**
	 * @param dni
	 * @param nivel
	 * @throws ExcepcionTransaccion
	 */
	public void motor(String dni, int nivel) throws ExcepcionTransaccion {
		filtrar_reglas(dni, nivel);
		concluir_reglas(dni, nivel);

	}

	/* PRIVADOS */
	private void aplicaRNivel0(int id, int[] categorias, int[] inferencias)
			throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		List<ReglasConclusiones> conclusiones = consulta.obtenConclusion(0, id);
		for (int i = 0; i < conclusiones.size(); i++) {
			String att = conclusiones.get(i).getId().getAtributo();
			String valor = conclusiones.get(i).getValor();
			int s = 0;
			int ind = 0;
			if (valor.equals("Muy Baja"))
				s = 1;
			else if (valor.equals("Baja"))
				s = 2;
			else if (valor.equals("Media-Baja"))
				s = 3;
			else if (valor.equals("Media"))
				s = 4;
			else if (valor.equals("Media-Alta"))
				s = 5;
			else if (valor.equals("Alta"))
				s = 6;
			else if (valor.equals("Muy Alta"))
				s = 7;
			if (att.equals("categoria_cultural"))
				ind = 0;
			else if (att.equals("categoria_economica"))
				ind = 1;
			else if (att.equals("categoria_profesional"))
				ind = 2;
			else if (att.equals("categoria_social"))
				ind = 3;
			categorias[ind] = categorias[ind] + s;
			inferencias[ind]++;
		}
	}

	private void aplicaRNivel1(int id, int[] sistemas, int[] inferencias)
			throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		List<ReglasConclusiones> conclusiones = consulta.obtenConclusion(1, id);
		for (int i = 0; i < conclusiones.size(); i++) {
			String att = conclusiones.get(i).getId().getAtributo();
			String valor = conclusiones.get(i).getValor();
			int s = 0;
			int ind = 0;
			if (valor.equals("Sano"))
				s = 0;
			else if (valor.equals("Leve"))
				s = 2;
			else if (valor.equals("Moderado"))
				s = 6;
			else if (valor.equals("Grave"))
				s = 12;
			else if (valor.equals("Critico"))
				s = 24;
			if (att.equals("sistema_muscular"))
				ind = 0;
			else if (att.equals("sistema_oseo"))
				ind = 1;
			else if (att.equals("sistema_articular"))
				ind = 2;
			else if (att.equals("sistema_nervioso"))
				ind = 3;
			else if (att.equals("sistema_digestivo"))
				ind = 4;
			else if (att.equals("sistema_excretor"))
				ind = 5;
			else if (att.equals("sistema_inmunitario"))
				ind = 6;
			else if (att.equals("sistema_tegumentario"))
				ind = 7;
			else if (att.equals("sistema_reproductor"))
				ind = 8;
			else if (att.equals("sistema_respiratorio"))
				ind = 9;
			else if (att.equals("sistema_cardiovascular"))
				ind = 10;
			else if (att.equals("sistema_linfatico"))
				ind = 11;
			else if (att.equals("estado_mental"))
				ind = 12;

			sistemas[ind] = sistemas[ind] + s;
			inferencias[ind]++;
		}
	}

	private void aplicaRNivel2(int id) throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		List<ReglasConclusiones> conclusiones = consulta.obtenConclusion(2, id);
		for (int i = 0; i < conclusiones.size(); i++) {
			// String att = conclusiones.get(i).getId().getAtributo();
			String valor = conclusiones.get(i).getValor();
			consulta.nuevoSintoma(Controlador.dni, Integer.valueOf(valor));
		}
	}

	private void aplicaRNivel3(int id) throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		List<ReglasConclusiones> conclusiones = consulta.obtenConclusion(3, id);
		int cod = 0;
		String estado = "";
		int coste = 0;
		for (int i = 0; i < conclusiones.size(); i++) {
			// String att = conclusiones.get(i).getId().getAtributo();
			String valor = conclusiones.get(i).getValor();
			switch (i) {
			case 0: {
				cod = Integer.valueOf(valor);
				break;
			}
			case 1: {
				coste = Integer.valueOf(valor);
				break;
			}
			case 2: {
				estado = valor;
				break;
			}
			}
		}
		consulta.nuevaPatologia(Controlador.dni, cod, estado, coste);
	}

	private void aplicaRNivel4(int id) throws ExcepcionTransaccion {
		MotorConsulta consulta = new MotorConsulta();
		List<ReglasConclusiones> conclusiones = consulta.obtenConclusion(4, id);
		int cod = 0;
		int porcentaje = 0;
		String conclusion = "";
		String tiempo = "";
		for (int i = 0; i < conclusiones.size(); i++) {
			// String att = conclusiones.get(i).getId().getAtributo();
			String valor = conclusiones.get(i).getValor();
			switch (i) {
			case 0: {
				cod = Integer.valueOf(valor);
				break;
			}
			case 1: {
				conclusion = valor;
				break;
			}
			case 2: {
				porcentaje = Integer.valueOf(valor);
				break;
			}
			case 3: {
				tiempo = valor;
				break;
			}
			}
		}
		consulta.nuevoResultado(Controlador.dni, cod, conclusion, tiempo,
				porcentaje);
	}

	private void concluyeNivel0(List<Reglas> reglasAct, String dni)
			throws ExcepcionTransaccion {
		int[] categorias = new int[4];
		int[] inferencias = new int[4];
		for (int i = 0; i < 4; i++) {
			categorias[i] = 0;
			inferencias[i] = 0;
		}
		for (int i = 0; i < reglasAct.size(); i++) {
			Reglas r = reglasAct.get(i);
			aplicaRNivel0(r.getId().getIdRegla(), categorias, inferencias);
		}
		guardaPerfilPersona(dni, categorias, inferencias);
	}

	private void concluyeNivel1(List<Reglas> reglasAct, String dni)
			throws ExcepcionTransaccion {
		int[] sistemas = new int[13];
		int[] inferencias = new int[13];
		for (int i = 0; i < 13; i++) {
			sistemas[i] = 0;
			inferencias[i] = 0;
		}
		for (int i = 0; i < reglasAct.size(); i++) {
			Reglas r = reglasAct.get(i);
			aplicaRNivel1(r.getId().getIdRegla(), sistemas, inferencias);
		}
		guardaPerfilClinico(dni, sistemas, inferencias);
	}

	private void concluyeNivel2(List<Reglas> reglasAct, String dni)
			throws ExcepcionTransaccion {
		for (int i = 0; i < reglasAct.size(); i++) {
			Reglas r = reglasAct.get(i);
			aplicaRNivel2(r.getId().getIdRegla());
		}
	}

	private void concluyeNivel3(List<Reglas> reglasAct, String dni)
			throws ExcepcionTransaccion {
		for (int i = 0; i < reglasAct.size(); i++) {
			Reglas r = reglasAct.get(i);
			aplicaRNivel3(r.getId().getIdRegla());
		}
	}

	private void concluyeNivel4(List<Reglas> reglasAct, String dni)
			throws ExcepcionTransaccion {
		for (int i = 0; i < reglasAct.size(); i++) {
			Reglas r = reglasAct.get(i);
			aplicaRNivel4(r.getId().getIdRegla());
		}
	}
}
